/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO

Authors: Nitay Megides
		 Rachel Banino

Project: Exercise 3

Using:	
		

Description:

oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/

#define _CRT_SECURE_NO_WARNINGS

#include "Loader.h"
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "General.h"
#include "ThreadsafeLinkedList.h"
#include "Manager.h"


#define MAX_BRIGHTNESS_BETWEEN_PIXELS 50
typedef BasicType TPixel;

PList FaceList;
PList ThreadList;

DWORD WINAPI ComparerMain(void* ptr);

BOOL ComparerInitialize()
{
	// Initialize the facebook
	FaceList = InitializeList();
	if (FaceList == NULL)
	{
		REPORT_ERROR("Comparer: Could not initialize list. Out of memory?\n");
		return FALSE;
	}

	ThreadList = InitializeList();
	if (ThreadList == NULL)
	{
		REPORT_ERROR("Comparer: Could not initialize list. Out of memory?\n");
		return FALSE;
	}

	return TRUE;
}


void ComparerFree()
{
	HANDLE* threads = (HANDLE*)malloc(sizeof(HANDLE) * ThreadList->Count);
	int i = 0;
	int threadCount = ThreadList->Count;
	if (threads == NULL)
	{
		REPORT_ERROR("Comparer: Out of memory!");
	}
	else
	{
		// Make sure that all threads are done
		PListItem item = ThreadList->Anchor;
		while (item != NULL)
		{
			threads[i] = (HANDLE)item->Value;
			i++;

			// Initialize the item value so the list won't try to free it
			item->Value = NULL;

			item = item->NextItem;

			// Just to make sure. Should never happen. But I'd like to know if it does
			if (i > threadCount)
			{
				REPORT_ERROR("Comparer: Threads count is bigger than expected. Zombie threads might exist");
				break;
			}
		}

		// Wait for all threads to finish
		if (WaitForMultipleMultipleObjects(threadCount, threads, INFINITE) != WAIT_OBJECT_0)
		{
			REPORT_ERROR("Comparer: WaitForMultipleObjects failed! Zombie threads might exist");
		}

		// We're done. We don't need those thread handles anymore
		for (i = 0; i < threadCount; i++)
			CloseHandle(threads[i]);

		free(threads);
	}

	// Free everything else
	FreeList(FaceList);
	FreeList(ThreadList);
}


BOOL FacebookAddAndCompare(PArea face)
{
	HANDLE threadHandle = NULL;
	DWORD threadID = 0;
	PListItem newFaceItem = NULL;

	// Allocate a new room for a face
	PArea newFace = (PArea)malloc(sizeof(TArea));
	if (newFace == NULL)
	{
		REPORT_ERROR("Comparer: Error allocating face area. Out of memory!\n");
		return FALSE;
	}

	// Create a copy of the new face
	memcpy(newFace, face, sizeof(TArea));

	// Add the new face
	newFaceItem = AddValue(FaceList, newFace);
	if (newFaceItem == NULL)
	{
		REPORT_ERROR("Comparer: Error adding face to facebook!\n");
		return FALSE;
	}

	// Create the thread that will compare the new face to the already-found faces
	threadHandle = CreateThread(NULL, 0, ComparerMain, newFaceItem, CREATE_SUSPENDED, &threadID);
	if (threadHandle == NULL)
	{
		REPORT_ERROR("Comparer: Couldn't create thread\n");
		return FALSE;
	}

	AddValue(ThreadList, threadHandle);

	// And start it
	ResumeThread(threadHandle);

	return TRUE;
}

BOOL CompareFaces(PArea face1, PArea face2)
{
	int nonMatchingPixels;
	int x, y;
	TPixel pixel1, pixel2;

	nonMatchingPixels = 0;
	// For all pixels in the picture
	for (x = 0; x < face1->Width; x++)
	{
		for (y = 0; y < face1->Height; y++)
		{
			// Get pixels
			pixel1 = GlobalPicture.Picture[face1->Y + y][face1->X + x];
			pixel2 = GlobalPicture.Picture[face2->Y + y][face2->X + x];

			// First check if they are the same
			if (pixel1 != pixel2)
			{
				nonMatchingPixels++;
				// Not the same. Are they at least close??
				if (abs(pixel1 - pixel2) > MAX_BRIGHTNESS_BETWEEN_PIXELS)
				{
					// No? Then it's not the same face
					return FALSE;
				}
			}
		}
	}

	// Okay. All pixels are same or close.
	// If we passed our bad pixels limit - return false. Otherwise - It's the same face
	return !(nonMatchingPixels > GlobalParameters.MaxDiffPixelsBetweenFaces);
}

DWORD WINAPI ComparerMain(void* ptr)
{
	PListItem referenceFaceItem = (PListItem)ptr;
	PListItem listItem = FaceList->Anchor;
	PArea referenceFace = NULL;
	PArea face = NULL;

	// Wait first
	WaitForRunPermission();

	// Get ready
	referenceFace = (PArea)referenceFaceItem->Value;

	// Loop on all faces
	while ((!ProgramTerminated()) && (listItem != NULL))
	{
		// Don't run on the same face (duh!)
		if (listItem != referenceFaceItem)
		{
			// Get the face from the list item
			face = (PArea)listItem->Value;

			// Check for identical faces
			if (CompareFaces(face, referenceFace))
			{
				ReportIdenticalFacesFound(referenceFace, face);
				REPORT_DEBUG("Comparer: Identical faces found at %d, %d and at %d, %d\n", referenceFace->X, referenceFace->Y, face->X, face->Y);
				REPORT_ERROR("Identical Faces Found!\n");
				TerminateProgram();
				break;
			}
		}
		listItem = GetNext(listItem);
	}

	// We're done here
	ReleaseRunPermission();

	return 0;
}

int FacesCount()
{
	return FaceList->Count;
}

void SaveFacebookToFile( char* filename )
{
	PListItem listItem = FaceList->Anchor;
	FILE* file = fopen(filename, "w");
	PArea face;

	if (file == NULL)
		return;

	while (listItem != NULL)
	{
		face = (PArea)listItem->Value;

		fprintf(file, "%d,%d\n", face->X, face->Y);

		listItem = GetNext(listItem);
	}

	fclose(file);
}
